#! /usr/bin/env python
#
# Copyright 2008 Spencer Davis <SpencerDavis91@gmail.com>
# Copyright 2008 Charles Connell <charles@connells.org>
#
# Recuerde is a simple list manager.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
# 
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program; if not, see <http://gnu.org/licenses/.>
#

import wx
from recuerde import List
from recuerde import Item
from database import Database
from adddialog import AddDialog
from editdialog import EditDialog
from addlistdialog import AddListDialog
from itempreviewpopup import ItemPreviewPopup

# These IDs are used to track events
ID_ADD_LIST = 1000
ID_DELETE_LIST = 1001
ID_TIMER_ITEM_PREVIEW_POPUP = 1002

class MainWindow(wx.Frame):
    """Main window of Recuerde, shows items in their lists."""
    def __init__(self, db, parent, id, title):
        """Create main window."""
        wx.Frame.__init__(self, parent, id, title)

        # Set up database
        self.db = Database()

        # Set window from file
        icon = wx.Icon('Recuerde.ico', wx.BITMAP_TYPE_ICO)
        self.SetIcon(icon)

        # Set up handler for window's close button
        self.Bind(wx.EVT_CLOSE, self.OnClose)

        # Set up file menu
        fileMenu = wx.Menu()
        fileMenu.Append(wx.ID_ADD, "&Add Item\tCtrl+A", "  Add new item to current list")
        fileMenu.Append(wx.ID_EDIT, "&Edit Item\tCtrl+E", " Edit current item")
        fileMenu.Append(wx.ID_DELETE, "&Delete Item\tCtrl+D", " Delete current item")
        fileMenu.AppendSeparator()
        fileMenu.Append(wx.ID_REFRESH, "&Refresh\tCtrl+R", "Refresh list views")
        fileMenu.AppendSeparator()
        fileMenu.Append(ID_ADD_LIST, "N&ew List", "  Add new item to current list")
        fileMenu.Append(ID_DELETE_LIST, "De&lete List", " Delete current item")
        fileMenu.AppendSeparator()
        fileMenu.Append(wx.ID_EXIT, "&Quit\tCtrl+Q"," Terminate the program")

        # Set up help menu
        helpMenu = wx.Menu()
        helpMenu.Append(wx.ID_ABOUT, "&About"," Information about this program")

        # Creating the menubar.
        menuBar = wx.MenuBar()
        # Add file and help menus to menubar
        menuBar.Append(fileMenu, "&Recuerde")
        menuBar.Append(helpMenu, "&Help")
        # Install menubar in window
        self.SetMenuBar(menuBar)
        # Catch events from menu items with event handlers
        wx.EVT_MENU(self, wx.ID_ABOUT, self.OnAbout)
        wx.EVT_MENU(self, wx.ID_EXIT, self.OnExit)
        wx.EVT_MENU(self, wx.ID_ADD, self.OnAdd)
        wx.EVT_MENU(self, wx.ID_EDIT, self.OnEdit)
        wx.EVT_MENU(self, wx.ID_DELETE, self.OnDelete)
        wx.EVT_MENU(self, wx.ID_REFRESH, self.OnRefresh)
        wx.EVT_MENU(self, ID_ADD_LIST, self.OnAddList)
        wx.EVT_MENU(self, ID_DELETE_LIST, self.OnDeleteList)

        # Create the notebook
        self.notebook = wx.Notebook(self)
        # Put notebook in a grid sizer to keep it expanded to the full size of the window
        notebookSizer = wx.GridSizer(0, 0, 0, 0)
        notebookSizer.Add(self.notebook, 0, wx.EXPAND, 0)
        # Use sizer
        self.SetSizer (notebookSizer)

        # Read tasks from database file into memory
        self.db.ReadTasks()

        # Create notebook pages with items in them
        self.RefreshAll()

        # Show this window
        self.Show(True)

    def RefreshAll(self):
        """Refreshes all the lists of tasks"""

        # Remember what page we are on
        currentPage = self.notebook.GetSelection()

        # Destroy all the list views
        self.notebook.DeleteAllPages()
        for list in self.db.GetLists():
            # Create a new list view in the notebook, with no headers, no editing allowed, and faint lines between each row
            listView = wx.ListView(self.notebook, style=(wx.LC_REPORT | wx.LC_NO_HEADER | wx.LC_HRULES))
            # If a key is pressed on the list, called ListKeyHandler
            listView.Bind(wx.EVT_KEY_DOWN, self.ListKeyHandler)
            # If a list item is double-clicked, called ListItemActivatedHandler
            listView.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.ListItemActivatedHandler)
            # If a list item is clicked, call ListClickHandler
            listView.Bind(wx.EVT_LEFT_DOWN, self.ListClickHandler)
            # Add listview to notebook
            self.notebook.AddPage(listView, list.name)
            # Add one column to the list view
            listView.InsertColumn(0, list.name)
            # Add each item in the list's name to the list view
            for item in list.GetItems():
                listView.Append([item.GetName()])
            # Adjust the listview's column's width to that of the longest row
            listView.SetColumnWidth(0, -1)

        # If our old page is a valid number, turn to that page so we don't confuse the user every time we refresh
        if currentPage > 0:
            self.notebook.SetSelection(currentPage)

    def RefreshList(self, listIndex):
        """Refreshes one list of items"""

        # Get the listview we are refreshing
        listView = self.notebook.GetPage(listIndex)
        # Remove all rows from it
        listView.DeleteAllItems()
        # For each item in the list, add it's name to the list view
        for item in self.db.GetLists()[listIndex].GetItems():
            listView.Append([item.GetName()])
        # Adjust the listview's column's width to that of the longest row
        listView.SetColumnWidth(0, -1)

    def ListKeyHandler(self, event):
        """Handle key events in a listview."""
        # Get the key
        key = event.GetKeyCode()

        # If it was the delete key, run the delete method to delete the currently selected item
        if key == wx.WXK_DELETE:
            self.OnDelete(event)
        # If it was the enter key, run the edit method to edit the currently selected item
        elif key == wx.WXK_RETURN:
            self.OnEdit(event)
        # Otherwise, continue propagation of the key event
        else:
            event.Skip()

    def ListItemActivatedHandler(self, event):
        """Handle double-clicks in list view."""
        # Stop the timer waiting for a double-click not to happen
        self.itemPreviewPopupTimer.Stop()
        # Run the edit method to edit the currently selected item
        self.OnEdit(event)

    def ListClickHandler(self, event):
        """Handle left-clicks in list view."""
        # Remember the point the click was at for use in ShowItemPreviewPopup
        self.listClickPoint = event.GetPosition()
        # Start a timer that will go off if there is no double-click before the timer expires
        # If there is no double click, we will show the item preview popup, but if there is,
        # we won't show it and run the edit dialog instead.
        self.itemPreviewPopupTimer = wx.Timer(self, ID_TIMER_ITEM_PREVIEW_POPUP)
        # Handle timer expiration event with ShowItemPreviewPopup
        wx.EVT_TIMER(self, ID_TIMER_ITEM_PREVIEW_POPUP, self.ShowItemPreviewPopup)
        # Start timer with 500 milliseconds to live
        self.itemPreviewPopupTimer.Start(500, True)
        # Continue propagation of click event
        event.Skip()

    def ShowItemPreviewPopup(self, event):
        """Show the item preview popup."""
        # If the event came from the item preview timer, continue
        if event.GetId() == ID_TIMER_ITEM_PREVIEW_POPUP:
            # If the click point was on an item in the list view, continue
            if self.notebook.GetCurrentPage().HitTest(self.listClickPoint)[0] is not -1:
                # Get the index of the list the item was in
                listIndex = self.notebook.GetSelection()
                # Get the index of the item in the list
                itemIndex = self.notebook.GetCurrentPage().HitTest(self.listClickPoint)[0]
                # Create the popup at the mouse click point, with the correct item
                ItemPreviewPopup(self, self.db.GetLists()[listIndex].GetItems()[itemIndex], pos=self.ClientToScreen(self.listClickPoint))

    def OnAdd(self, event):
        """Creates the dialog for adding an item."""
        # Create and run the dialog for adding a new item
        # The list is retrieved by the index of the current notebook page
        add = AddDialog(self.db.GetLists()[self.notebook.GetSelection()], self.db, self)
        # If the dialog was accepted and not canceled, refresh the list to show the new item
        if add.ShowModal() == wx.ID_ADD:
            self.RefreshList(self.notebook.GetSelection())

    def OnEdit(self, event):
        """Creates the dialog for editing the currently selected item."""
        # Make sure that there is a currently selected item.
        if self.notebook.GetCurrentPage().GetFirstSelected() is not -1:
            # Get the index of the list the item was in
            listIndex = self.notebook.GetSelection()
            # Get the index of the item in the list
            itemIndex = self.notebook.GetCurrentPage().GetFirstSelected()
            # Create the edit dialog with the correct item
            edit = EditDialog(self.db.GetLists()[listIndex].GetItems()[itemIndex], self.db, self)
            # If the dialog was accepted and not canceled, refresh the list to show the updated item
            if edit.ShowModal() == wx.ID_EDIT:
                # Refresh the list
                self.RefreshList(listIndex)

    def OnDelete(self, event):
        """Deletes the currently selected item."""
        # Make sure that there is a currently selected item.
        if self.notebook.GetCurrentPage().GetFirstSelected() is not -1:
            # Get the index of the list the item was in
            listIndex = self.notebook.GetSelection()
            # Get the index of the item in the list
            itemIndex = self.notebook.GetCurrentPage().GetFirstSelected()
            # Get the item
            item = self.db.GetLists()[listIndex].GetItems()[itemIndex]
            # Delete the item from the database and from memory
            self.db.RemoveItem(item, self.db.GetLists()[listIndex])
            # Refresh the list
            self.RefreshList(listIndex)

    def OnAddList(self, event):
        """Creates the dialog to add a new list."""
        # Create the dialog
        add = AddListDialog(self.db, self)
        # If the dialog was accepted and not canceled, refresh the list to show the updated item
        if add.ShowModal() == wx.ID_ADD:
            # Refresh the lists
            self.RefreshAll()
            # Set the current notebook page to the last one, the one we just added
            self.notebook.SetSelection(self.notebook.GetPageCount() - 1)

    def OnDeleteList(self, event):
        """Deletes the currently selected list"""
        # Make sure that there is a currently selected list.
        if self.notebook.GetSelection() is not -1:
            # Get the index of the current list
            listIndex = self.notebook.GetSelection()
            # Get the list itself
            list = self.db.GetLists()[listIndex]
            # Remove the list from the database and from memory
            self.db.RemoveList(list)
            # Refresh the lists
            self.RefreshAll()

    def OnRefresh(self, event):
        """Calls refresh to refresh the lists of items."""
        self.RefreshAll()

    def OnAbout(self, event):
        # Create object that knows about info
        about = wx.AboutDialogInfo()
        about.SetName ('Recuerde')
        about.SetVersion ('0.2.0')
        about.SetDescription ('A list manager')
        about.SetCopyright ('Copyright 2008 by Spencer Davis and Charles Connell')
        about.SetWebSite ('http://recuerde.googlecode.com')
        about.AddDeveloper ('Spencer Davis - SpencerDavis91@gmail.com')
        about.AddDeveloper ('Charles Connell - charles@connells.org')

        # Make about dialog with info
        wx.AboutBox (about)

    def OnClose(self, event):
        self.Hide()

    def OnExit(self, event):
        # Quit the app
        wx.Exit()


